package com.linkoog.devtools.raw_index;

import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.extensions.ProjectExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

public interface PsiRawFileIndex<K extends PsiElement,  V extends PsiRawFileIndex.Item> extends UserDataHolder, Disposable {

    //ExtensionPointName<PsiRawFileIndex> EXTENSION_POINT_NAME = ExtensionPointName.create("com.github.linkoog.DevTools.psiElementCache");

    //ProjectExtensionPointName<PsiRawFileIndex> PROJECT_EP_NAME =  new ProjectExtensionPointName("com.github.linkoog.DevTools.psiElementCache");
    ProjectExtensionPointName<PsiRawFileIndex> EXTENSION_POINT_NAME =  new ProjectExtensionPointName("com.github.linkoog.DevTools.psiElementCache");


    default VirtualFile getVirtualFile(PsiElement psiElement){
        return ApplicationManager.getApplication().runReadAction((Computable<VirtualFile>) () -> psiElement.getContainingFile().getOriginalFile().getVirtualFile());
    }

    Key<Set<PsiElement>> getPsiElementKey();

    default Set<PsiElement> getPsiElementSet() {
        Set<PsiElement> psiElements = getUserData(getPsiElementKey());
        if (psiElements == null){
            psiElements = new CopyOnWriteArraySet<>();
            putUserData(getPsiElementKey(), psiElements);
        }
        return psiElements;
    }

    default void savePsiElement(PsiElement psiElement){
        getPsiElementSet().add(psiElement);
    }

    default void removePsiElement(PsiElement psiElement){
        getPsiElementSet().remove(psiElement);
    }

    default boolean isContainsPsiElement(PsiElement psiElement){
        return getPsiElementSet().contains(psiElement);
    }


    boolean isInit();

    void init(@NotNull Project project) throws Exception;

    default boolean isLasy(){
        return true;
    }



    default boolean support(PsiElement psiElement){
        return false;
    }

    V updateCache(@NotNull K key);

    void removeCache(@NotNull K key);

    /**
     *  第一次解析时，会比较耗时。
     */
    V getCache(@NotNull K key);

    V getCache(@NotNull String key);

    Collection<V> getCaches();

    String getCacheKey(@NotNull K key);

    String getPsiElementSign(@NotNull K key);

    V resolve(K k);

    void afterFileChange(PsiClass psiClass, boolean forceUpdate);

    void afterFileDelete(@NotNull VirtualFile virtualFile);


    interface Item<K extends PsiElement> {
        String getFilePath();
        int getPsiElementOffset();
        String getName();
        long getTimestamp();
        void setTimestamp(long timestamp);
        boolean initPsiElement(@NotNull Project project, @Nullable K psiElement);
        @NotNull K getPsiElement();
    }


}
